// This is the ``Mersenne Twister'' random number generator MT19937, which
// generates pseudorandom integers uniformly distributed in 0..(2^32 - 1)
// starting from any odd seed in 0..(2^32 - 1).  This version is a recode
// by Shawn Cokus (Cokus@math.washington.edu) on March 8, 1998 of a version by
// Takuji Nishimura (who had suggestions from Topher Cooper and Marc Rieffel in
// July-August 1997).
//
// Effectiveness of the recoding (on Goedel2.math.washington.edu, a DEC Alpha
// running OSF/1) using GCC -O3 as a compiler: before recoding: 51.6 sec. to
// generate 300 million random numbers; after recoding: 24.0 sec. for the same
// (i.e., 46.5% of original time), so speed is now about 12.5 million random
// number generations per second on this machine.
//
// According to the URL <http://www.math.keio.ac.jp/~matumoto/emt.html>
// (and paraphrasing a bit in places), the Mersenne Twister is ``designed
// with consideration of the flaws of various existing generators,'' has
// a period of 2^19937 - 1, gives a sequence that is 623-dimensionally
// equidistributed, and ``has passed many stringent tests, including the
// die-hard test of G. Marsaglia and the load test of P. Hellekalek and
// S. Wegenkittl.''  It is efficient in memory usage (typically using 2506
// to 5012 bytes of static data, depending on data type sizes, and the code
// is quite short as well).  It generates random numbers in batches of 624
// at a time, so the caching and pipelining of modern systems is exploited.
// It is also divide- and mod-free.
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Library General Public License as published by
// the Free Software Foundation (either version 2 of the License or, at your
// option, any later version).  This library is distributed in the hope that
// it will be useful, but WITHOUT ANY WARRANTY, without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
// the GNU Library General Public License for more details.  You should have
// received a copy of the GNU Library General Public License along with this
// library; if not, write to the Free Software Foundation, Inc., 59 Temple
// Place, Suite 330, Boston, MA 02111-1307, USA.
//
// The code as Shawn received it included the following notice:
//
//   Copyright (C) 1997 Makoto Matsumoto and Takuji Nishimura.  When
//   you use this, send an e-mail to <matumoto@math.keio.ac.jp> with
//   an appropriate reference to your work.
//
// It would be nice to CC: <Cokus@math.washington.edu> when you write.
//
#ifndef _LIB_RAND_H
#define _LIB_RAND_H

#include "math/vec2.h"
#include "math/vec3.h"

namespace num
{

#define _RAND_N              (624)                 // length of state vector
#define _RAND_M              (397)                 // a period parameter
#define _RAND_K              (0x9908B0DFU)         // a magic constant
#define hiBit(u)       ((u) & 0x80000000U)   // mask all but highest   bit of u
#define loBit(u)       ((u) & 0x00000001U)   // mask all but lowest    bit of u
#define loBits(u)      ((u) & 0x7FFFFFFFU)   // mask     the highest   bit of u
#define mixBits(u, v)  (hiBit(u)|loBits(v))  // move hi bit of u to hi bit of v


class RAND32
{
public:
	// objects that want to can also hold onto their own random generator, for example for network
    RAND32(void);
	~RAND32(void);

	
	/**
     * Singleton accessor
     */
    static RAND32&      Instance();

    /**
     * Retrieve a random int between 0..MAX_INT
     * @return random number
     */
    int        RandInt();

    /**
     * Retrieve a random int between min..max
     * @return random number
     */
    int        RandInt( int min, int max);

    /**
     * Retrieve a random float between 0..1
     * @return random number
     */
    float               RandFloat();

    /**
     * Retrieve a random float between min..max
     * @return random number
     */
    float               RandFloat(float min, float max);

    /**
     * Retrieve a random double between 0..1
     * @return random number
     */
    double              RandDouble();

    /**
     * Retrieve a random double between min..max
     * @return random number
     */
    double              RandDouble(double min, double max);

    /**
     * Retrieve a random Vec2 where each element in the vector is a random 0..1
     * @return random vector
     */
    math::Vec2          RandVec2();

    /**
     * Retrieve a random Vec2 where each element in the vector is a random min..max
     * @return random vector
     */
    math::Vec2          RandVec2(float min, float max);

    /**
     * Retrieve a random Vec2 where each element result.xxx in the vector is a random number
     * between the min.xxx and max.xxx. i.e. RandVec2(math::Vec2(0, 0), math::Vec2(1, 100))
     * might give a return math::Vec2(0.2, 50);
     * @return random vector
     */
    math::Vec2          RandVec2(const math::Vec2& min, const math::Vec2& max);

    /**
     * Retrieve a random Vec3 where each element in the vector is a random 0..1
     * @return random vector
     */
    math::Vec3          RandVec3();


    /**
     * Retrieve a random Vec3 where each element in the vector is a random min..max
     * @return random vector
     */
    math::Vec3          RandVec3(float min, float max);

    /**
     * Retrieve a random Vec3 where each element result.xxx in the vector is a random number
     * between the min.xxx and max.xxx. See RandVec2 for details
     * @return random vector
     */
    math::Vec3          RandVec3(const math::Vec3& min, const math::Vec3& max);

    /**
     * Reseed the random number generator
     * @param seed		New Seed
     */
    void                seedMT(unsigned long seed);
	/**** 
	* @brief:	Get a weighter int between from the set weights
	* @param:	Weights	Weighted set
	* @param:	Count	Number of entries
	* @returns: random result
	*/
	int					GetWeightedRand(int	Weights[], int Count)
	{
		int Sum = 0;
		for (int i=0; i<Count; i++)
			Sum +=Weights[i];

		int	Val = RandInt() % Sum;

		for (int i=0; i<Count; i++)
		{
			Val -= Weights[i];
			if (Val < 0)
				return i;
		}
		return 0;
	}
private:


    /// Not implemented to prevent copying
    RAND32( const RAND32 & );     
    RAND32& operator=( const RAND32& );


    unsigned long       reloadMT(void);
    unsigned long       randomMT(void);


    unsigned long       state[_RAND_N+1];        /// state vector + 1 extra to not violate ANSI C
    unsigned long *     next;                       /// next random value is computed from here
    int                 left;                       /// can *next++ this many times before reloading

}; // RAND32



} // end namespace
#endif // _LIB_RAND_H